package com.jneagle.xlstool.bzwgjmx.impl.dao;

import com.dwarfeng.subgrade.sdk.interceptor.analyse.BehaviorAnalyse;
import com.dwarfeng.subgrade.sdk.interceptor.analyse.SkipRecord;
import com.dwarfeng.subgrade.stack.bean.dto.PagingInfo;
import com.dwarfeng.subgrade.stack.bean.key.LongIdKey;
import com.dwarfeng.subgrade.stack.exception.DaoException;
import com.jneagle.xlstool.bzwgjmx.stack.bean.entity.RawPart;
import com.jneagle.xlstool.bzwgjmx.stack.dao.RawPartDao;
import com.jneagle.xlstool.bzwgjmx.stack.service.RawPartMaintainService;
import org.dozer.Mapper;
import org.springframework.stereotype.Repository;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

@Repository
public class RawPartDaoImpl implements RawPartDao {

    private final Mapper mapper;

    private final Map<LongIdKey, RawPart> memory = new LinkedHashMap<>();

    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private final List<RawPart> conflictIndex = new ArrayList<>();
    private final List<RawPart> notAppendIndex = new ArrayList<>();
    private final List<RawPart> appendIndex = new ArrayList<>();

    private boolean indexReadyFlag = false;

    public RawPartDaoImpl(Mapper mapper) {
        this.mapper = mapper;
    }

    @Override
    @BehaviorAnalyse
    public void clear() {
        lock.writeLock().lock();
        try {
            memory.clear();
        } finally {
            lock.writeLock().unlock();
        }
    }

    @Override
    @BehaviorAnalyse
    public boolean exists(LongIdKey key) {
        lock.readLock().lock();
        try {
            return memory.containsKey(key);
        } finally {
            lock.readLock().unlock();
        }
    }

    @Override
    @BehaviorAnalyse
    public RawPart get(LongIdKey key) throws DaoException {
        lock.readLock().lock();
        try {
            return cloneBean(memory.get(key));
        } finally {
            lock.readLock().unlock();
        }
    }

    @Override
    @BehaviorAnalyse
    public LongIdKey insert(RawPart element) throws DaoException {
        lock.writeLock().lock();
        try {
            indexReadyFlag = false;
            RawPart neoElement = cloneBean(element);
            memory.put(neoElement.getKey(), neoElement);
            return neoElement.getKey();
        } finally {
            lock.writeLock().unlock();
        }
    }

    @Override
    @BehaviorAnalyse
    public void update(RawPart element) throws DaoException {
        lock.writeLock().lock();
        try {
            indexReadyFlag = false;
            RawPart neoElement = cloneBean(element);
            memory.put(neoElement.getKey(), neoElement);
        } finally {
            lock.writeLock().unlock();
        }
    }

    @Override
    @BehaviorAnalyse
    public void delete(LongIdKey key) {
        lock.writeLock().lock();
        try {
            indexReadyFlag = false;
            memory.keySet().remove(key);
        } finally {
            lock.writeLock().unlock();
        }
    }

    @Override
    @BehaviorAnalyse
    public boolean allExists(@SkipRecord List<LongIdKey> keys) {
        lock.readLock().lock();
        try {
            return memory.keySet().containsAll(keys);
        } finally {
            lock.readLock().unlock();
        }
    }

    @Override
    @BehaviorAnalyse
    public boolean nonExists(@SkipRecord List<LongIdKey> keys) {
        lock.readLock().lock();
        try {
            for (LongIdKey key : keys) {
                if (memory.containsKey(key)) {
                    return false;
                }
            }
            return true;
        } finally {
            lock.readLock().unlock();
        }
    }

    @Override
    @BehaviorAnalyse
    @SkipRecord
    public List<RawPart> batchGet(@SkipRecord List<LongIdKey> keys) throws DaoException {
        lock.readLock().lock();
        try {
            List<RawPart> result = new ArrayList<>();
            for (LongIdKey key : keys) {
                RawPart rawPart = cloneBean(memory.get(key));
                result.add(rawPart);
            }
            return result;
        } finally {
            lock.readLock().unlock();
        }
    }

    @Override
    @BehaviorAnalyse
    @SkipRecord
    public List<LongIdKey> batchInsert(@SkipRecord List<RawPart> elements) throws DaoException {
        lock.writeLock().lock();
        try {
            indexReadyFlag = false;
            List<LongIdKey> result = new ArrayList<>();
            for (RawPart element : elements) {
                RawPart neoElement = cloneBean(element);
                memory.put(neoElement.getKey(), neoElement);
                result.add(neoElement.getKey());
            }
            return result;
        } finally {
            lock.writeLock().unlock();
        }
    }

    @Override
    @BehaviorAnalyse
    public void batchUpdate(@SkipRecord List<RawPart> elements) throws DaoException {
        lock.writeLock().lock();
        try {
            indexReadyFlag = false;
            for (RawPart element : elements) {
                RawPart neoElement = cloneBean(element);
                memory.put(neoElement.getKey(), neoElement);
            }
        } finally {
            lock.writeLock().unlock();
        }
    }

    @Override
    @BehaviorAnalyse
    public void batchDelete(@SkipRecord List<LongIdKey> keys) {
        indexReadyFlag = false;
        lock.writeLock().lock();
        try {
            keys.forEach(memory.keySet()::remove);
        } finally {
            lock.writeLock().unlock();
        }
    }

    @Override
    @BehaviorAnalyse
    @SkipRecord
    public List<RawPart> lookup() throws DaoException {
        lock.readLock().lock();
        try {
            List<RawPart> result = new ArrayList<>();
            for (RawPart rawPart : memory.values()) {
                result.add(cloneBean(rawPart));
            }
            return result;
        } finally {
            lock.readLock().unlock();
        }
    }

    @Override
    @BehaviorAnalyse
    @SkipRecord
    public List<RawPart> lookup(PagingInfo pagingInfo) throws DaoException {
        lock.readLock().lock();
        try {
            int beginIndex = pagingInfo.getPage() * pagingInfo.getRows();
            int endIndex = beginIndex + pagingInfo.getRows();
            List<RawPart> subList = new ArrayList<>(memory.values()).subList(beginIndex, endIndex);
            List<RawPart> result = new ArrayList<>();
            for (RawPart rawPart : subList) {
                result.add(cloneBean(rawPart));
            }
            return result;
        } finally {
            lock.readLock().unlock();
        }
    }

    @Override
    @BehaviorAnalyse
    public int lookupCount() {
        lock.readLock().lock();
        try {
            return memory.size();
        } finally {
            lock.readLock().unlock();
        }
    }

    @Override
    public List<RawPart> lookup(String preset, Object[] objs) throws DaoException {
        lock.readLock().lock();
        try {
            mayBuildIndex();
            switch (preset) {
                case RawPartMaintainService.CONFLICT:
                    return conflict();
                case RawPartMaintainService.NOT_APPEND:
                    return notAppend();
                case RawPartMaintainService.APPEND:
                    return append();
                default:
                    throw new DaoException("未知的预设: " + preset);
            }
        } finally {
            lock.readLock().unlock();
        }
    }

    @Override
    public List<RawPart> lookup(String preset, Object[] objs, PagingInfo pagingInfo) throws DaoException {
        throw new DaoException("not implemented yet");
    }

    @Override
    public int lookupCount(String preset, Object[] objs) throws DaoException {
        lock.readLock().lock();
        try {
            mayBuildIndex();
            switch (preset) {
                case RawPartMaintainService.CONFLICT:
                    return conflictCount();
                case RawPartMaintainService.NOT_APPEND:
                    return notAppendCount();
                case RawPartMaintainService.APPEND:
                    return appendCount();
                default:
                    throw new DaoException("未知的预设: " + preset);
            }
        } finally {
            lock.readLock().unlock();
        }
    }

    private List<RawPart> conflict() throws DaoException {
        List<RawPart> result = new ArrayList<>();
        for (RawPart rawPart : conflictIndex) {
            result.add(cloneBean(rawPart));
        }
        return result;
    }

    private int conflictCount() {
        return conflictIndex.size();
    }

    private List<RawPart> notAppend() throws DaoException {
        List<RawPart> result = new ArrayList<>();
        for (RawPart rawPart : notAppendIndex) {
            result.add(cloneBean(rawPart));
        }
        return result;
    }

    private List<RawPart> append() throws DaoException {
        List<RawPart> result = new ArrayList<>();
        for (RawPart rawPart : appendIndex) {
            result.add(cloneBean(rawPart));
        }
        return result;
    }

    private int notAppendCount() {
        return notAppendIndex.size();
    }

    private int appendCount() {
        return appendIndex.size();
    }

    private void mayBuildIndex() {
        if (indexReadyFlag) {
            return;
        }

        conflictIndex.clear();
        notAppendIndex.clear();
        appendIndex.clear();
        for (RawPart rawPart : memory.values()) {
            if (rawPart.isConflictFlag()) {
                conflictIndex.add(rawPart);
            }
            if (!rawPart.isAppendFlag()) {
                notAppendIndex.add(rawPart);
            } else {
                appendIndex.add(rawPart);
            }
        }

        indexReadyFlag = true;
    }

    private RawPart cloneBean(RawPart rawPart) throws DaoException {
        try {
            return mapper.map(rawPart, RawPart.class);
        } catch (Exception e) {
            throw new DaoException(e);
        }
    }
}
